﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
using System.Windows.Forms;


namespace ClientATM
{
    public delegate void WrittenMethod(ArgList args);
    public class ComParser
    {
        ArgList args = new ArgList();
        List<ComFormat> coms = new List<ComFormat>();

        public void AddFormat(string mask, WrittenMethod method) { coms.Add(new ComFormat(mask, method)); }
        public Boolean Match(string toCheck, string toComp)
        {
            string checkRem, compRem, compBit;
            int obi, cbi, biti;
            checkRem = toCheck; compRem = toComp;

            obi = compRem.IndexOf('['); cbi = compRem.IndexOf(']');
            while (compRem != "")
            {
                obi = compRem.IndexOf('['); cbi = compRem.IndexOf(']');
                if (obi > -1 && cbi > -1)
                {
                    compBit = compRem.Substring(0, obi);
                    compRem = compRem.Substring(Math.Min(compRem.Length, cbi + 1));
                }
                else
                {
                    compBit = compRem;
                    compRem = "";
                }
                biti = checkRem.IndexOf(compBit);
                if (biti == -1)
                    return false;
                checkRem = checkRem.Substring(biti + compBit.Length);
            }
            return true;
        }

        public Boolean Execute(string toExecute)
        {
            foreach (ComFormat format in coms)
            {
                if (ExecuteSingle(toExecute, format.gMask(), format.gMethod())) return true;
            }
            return false;
        }
        public Boolean ExecuteSingle(string toExecute, string mask, WrittenMethod called)
        {
            args.Clear();
            string exRem, maskRem, maskBit;
            string prevName, curName;
            int obi, cbi, biti;
            bool first = true;
            exRem = toExecute; maskRem = mask;

            prevName = "";
            curName = "";

            biti = 0;

            obi = maskRem.IndexOf('['); cbi = maskRem.IndexOf(']');
            while (maskRem != "")
            {
                obi = maskRem.IndexOf('['); cbi = maskRem.IndexOf(']');
                if (obi > -1 && cbi > -1)
                {
                    maskBit = maskRem.Substring(0, obi);
                    curName = maskRem.Substring(obi + 1, cbi - (obi + 1));
                    maskRem = maskRem.Substring(Math.Min(maskRem.Length, cbi + 1));
                }
                else
                {
                    maskBit = maskRem;
                    curName = null;
                    maskRem = "";
                }
                biti = exRem.IndexOf(maskBit);
                if (biti == -1) return false;
                if (first && biti != 0) return false;
                if (!first) args.Add(prevName, exRem.Substring(0, biti).Trim());
                exRem = exRem.Substring(biti + maskBit.Length);

                first = false;
                prevName = curName;
            }
            if (prevName != null) args.Add(prevName, exRem);

            called(args);

            return true;
        }
    }
    public class ArgBit
    {
        public string name;
        public string value;
        public ArgBit(string newName, string newValue)
        {
            name = newName;
            value = newValue;
        }
    }
    public class ArgList
    {
        List<ArgBit> content = new List<ArgBit>();
        public void Clear()
        {
            content.Clear();
        }
        public void Add(string name, string value)
        {
            content.Add(new ArgBit(name, value));
        }
        public string Get(string key)
        {
            foreach (ArgBit arg in content)
            {
                if (arg.name == key)
                    return arg.value;
            }
            return null;
        }
    }
    public class ComFormat
    {
        string mask;
        WrittenMethod method;
        public ComFormat(string newMask, WrittenMethod newMethod)
        {
            mask = newMask;
            method = newMethod;
        }
        public string gMask() { return mask; }
        public WrittenMethod gMethod() { return method; }
    }
//AGENT KLIENCKI
    public class ClientAgent
        {
        string clientName;

        TcpListener listener;
        Timer lt;
        Byte[] listenBuffer = new Byte[256];
        ComParser interpreter = new ComParser();
        Byte[] response = null;

        TcpClient notifier;
        IPEndPoint sipep;
        ClientATM myNode;

        public ClientAgent(string name, Int32 listenPort, Int32 sendPort, ClientATM node)
            {
            CLA(name, new IPEndPoint(IPAddress.Loopback, listenPort), new IPEndPoint(IPAddress.Loopback, sendPort), node);
            }
        public ClientAgent(string name, IPEndPoint toListen, IPEndPoint toSend, ClientATM node)
            {
            CLA(name, toListen, toSend, node);
            }
        public void CLA(string name, IPEndPoint toListen, IPEndPoint toSend, ClientATM node)
            {
            clientName = name;
            myNode = node;
            InitializeCommands();
            
            listener = new TcpListener(toListen);
            listener.Start();
            lt = new Timer();
            lt.Tick += new EventHandler(DoListen);
            lt.Interval = 50;
            lt.Start();

            sipep = toSend;
            this.SendNotify("KEEPALIVE:" + clientName);
            }
        public void DisposeListener()
            {
                listener.Stop();
                lt.Stop();
                
            }
        private void InitializeCommands()
            {
            interpreter.AddFormat("GET WHOIS", RespondWhois);
            interpreter.AddFormat("SET ENABLED:[tof]", EnableNode);
            }
        private void RespondWhois(ArgList args)
            {
            MakeResponse("GETRSP WHOIS:"+clientName);
            TMPSendResponse("GETRSP WHOIS:" + clientName);       //do wywalenia jak będzie wiele aplikacji
            }
        private void EnableNode(ArgList args)
            {
            if (args.Get("tof") == "TRUE")
                {
                /*
                myNode.setEnabled(true);
                if (myNode.getEnabled()) {
                    MakeResponse("SETRSP ENABLED:TRUE,OK");
                    }
                else {
                    MakeResponse("SETRSP ENABLED:FALSE,ERROR");
                    }
                */
                }
            if (args.Get("tof") == "TRUE")
                {
                /*
                myNode.setEnabled(false);
                if (!myNode.getEnabled()) {
                    MakeResponse("SETRSP ENABLED:FALSE,OK");
                    }
                else {
                    MakeResponse("SETRSP ENABLED:TRUE,ERROR");
                    }
                */
                }
            }
        //nasłuchiwanie na przychodzące polecenia
        private void DoListen(object sender, EventArgs e)
            {
            
if (listener.Pending())
                {
                TcpClient speaker = listener.AcceptTcpClient();
                NetworkStream s = speaker.GetStream();

                int count = s.Read(listenBuffer, 0, listenBuffer.Length);
                Call(Encoding.UTF8.GetString(listenBuffer, 0, count));
                //if (response != null) s.Write(response, 0, response.Length);
                        //do przywrócenia jak będzie wiele aplikacji
                response = null;
                }
            }
        public void Call(string command)
            {
            interpreter.Execute(command);
            }
        public void AmAlive()
            {
            this.SendNotify("KEEPALIVE:"+clientName);
            }
        private void SendNotify(string notify)
            {
            notifier = new TcpClient();
            notifier.SendBufferSize = 256;
            notifier.SendTimeout = 1000;
            try
                {
                notifier.Connect(sipep);
                Byte[] sendBytes = Encoding.UTF8.GetBytes("NOTIFY " + notify);
                NetworkStream s = notifier.GetStream();
                s.Write(sendBytes, 0, sendBytes.Length);
                }
            catch (SocketException)
                {
                myNode.Print("‼ Nie udało się skomunikować z zarządcą!");
                }
            notifier.Close();
            }
        private void TMPSendResponse(string notify)
            {
            notifier = new TcpClient();
            notifier.SendBufferSize = 256;
            notifier.Connect(sipep);
            Byte[] sendBytes = Encoding.UTF8.GetBytes(notify);
            NetworkStream s = notifier.GetStream();
            s.Write(sendBytes, 0, sendBytes.Length);
            notifier.Close();
            }
        private void MakeResponse(string rsp)
            {
            response = Encoding.UTF8.GetBytes(rsp);
            }
        }

}
